package com.huyi.common.annotation.aspect;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.huyi.common.annotation.ParamOff;
import com.huyi.common.annotation.RedisLock;
import com.huyi.common.cache.DistributedLockCache;

import lombok.extern.slf4j.Slf4j;

/**
 * 设置Redis切面
 * 
 * @author shengli.hu
 */
@Slf4j
@Aspect
@Component
public class RedisLockAspect {

	@Autowired
	private DistributedLockCache cache;

	@Pointcut("@annotation(com.huyi.common.annotation.RedisLock)")
	public void doPointcut() {
	}

	/**
	 * 在注解的方法之前执行
	 * 
	 * @see
	 */
	@Before("doPointcut()")
	public void beforeRedisLockAspect(JoinPoint joinPoint) {
		// 锁key
		String lockKey = "";
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			// 获取方法名
			Method method = signature.getMethod();
			// 获取注解类
			RedisLock lock = method.getAnnotation(RedisLock.class);
			// 参数对象注解
			ParamOff paramOff = lock.paramOff();
			String keyValue = "";
			if (paramOff != null) {
				String lockBean = paramOff.lockBean();
				String lockParam = paramOff.lockParam();
				if(StringUtils.isNotBlank(lockBean) || StringUtils.isNotBlank(lockParam)) {
					// 获取参数
					String classType = joinPoint.getTarget().getClass().getName();
					Class<?> clazz = Class.forName(classType);
					Map<String, Object> paramMap = getFieldsName(clazz, method.getName(), joinPoint.getArgs());
					// 判断参数是否存在
					if(StringUtils.isBlank(lockParam)) {
						return;
					}
					if(StringUtils.isNotBlank(lockBean)) {
						if (paramMap.containsKey(lockBean)) {
							Object object = paramMap.get(lockBean);
							JSONObject json = JSONObject.parseObject(object.toString());
							if(json.containsKey(lockParam)) {
								keyValue = json.getString(lockParam);
							}
						} else {
							return;
						}
					} else {
						if (paramMap.containsKey(lockParam)) {
							keyValue = paramMap.get(lockParam).toString();
						} else {
							return;
						}
					}
				}
			}

			// 组装锁
			if (StringUtils.isNotBlank(lock.lockKey())) {
				if (StringUtils.isNotBlank(keyValue)) {
					lockKey = lock.lockKey() + "-" + keyValue;
				} else {
					lockKey = lock.lockKey();
				}
			}

			// 加锁
			if (StringUtils.isNotBlank(lockKey)) {
				cache.tryLock(lockKey, lock.timeout());
			}
		} catch (Exception e) {
			log.error("【加锁】操作失败：", e);
		}
	}

	/**
	 * 在注解的方法之后执行
	 * 
	 * @see
	 */
	@After("doPointcut()")
	public void afterRedisLockAspect(JoinPoint joinPoint) {
		// 锁key
		String lockKey = "";
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			// 获取方法名
			Method method = signature.getMethod();
			// 获取注解类
			RedisLock lock = method.getAnnotation(RedisLock.class);
			// 参数对象注解
			ParamOff paramOff = lock.paramOff();
			String keyValue = "";
			if (paramOff != null) {
				String lockBean = paramOff.lockBean();
				String lockParam = paramOff.lockParam();
				if(StringUtils.isNotBlank(lockBean) || StringUtils.isNotBlank(lockParam)) {
					// 获取参数
					String classType = joinPoint.getTarget().getClass().getName();
					Class<?> clazz = Class.forName(classType);
					Map<String, Object> paramMap = getFieldsName(clazz, method.getName(), joinPoint.getArgs());
					// 判断参数是否存在
					if(StringUtils.isBlank(lockParam)) {
						return;
					}
					if(StringUtils.isNotBlank(lockBean)) {
						if (paramMap.containsKey(lockBean)) {
							Object object = paramMap.get(lockBean);
							JSONObject json = JSONObject.parseObject(object.toString());
							if(json.containsKey(lockParam)) {
								keyValue = json.getString(lockParam);
							}
						} else {
							return;
						}
					} else {
						if (paramMap.containsKey(lockParam)) {
							keyValue = paramMap.get(lockParam).toString();
						} else {
							return;
						}
					}
				}
			}

			// 组装锁
			if (StringUtils.isNotBlank(lock.lockKey())) {
				if (StringUtils.isNotBlank(keyValue)) {
					lockKey = lock.lockKey() + "-" + keyValue;
				} else {
					lockKey = lock.lockKey();
				}
			}
		} catch (Exception e) {
			log.error("【释放锁】操作失败：", e);
		} finally {
			// 释放锁锁
			if (StringUtils.isNotBlank(lockKey)) {
				cache.unlock(lockKey);
			}
		}
	}

	/**
	 * 获取字段名和字段值
	 * 
	 * @param clazz
	 * @param method
	 * @param args
	 * @return
	 */
	private Map<String, Object> getFieldsName(Class<?> clazz, String methodName, Object[] args) {
	    //获取方法参数名
		Map<String, Object> map = new HashMap<String, Object>();
	    LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
	    for (Method method : clazz.getDeclaredMethods()) {
	    	if(methodName.equals(method.getName())) {
	    	    String[] parameterNames =  discoverer.getParameterNames(method);
	    	    for (int i = 0; i < parameterNames.length; i++) {
	    	    	map.put(parameterNames[i], args[i]);
	    	    }
	    	}
	    }
		return map;
	}

}